<?php
/**
 * @package db
 */

/**
 * Interface of transaction locker used in DBConnector.
 * 
 * @see DBConnector
 */
interface DBLocker {
	public function __construct(bool $isStarter);
	public function getIsTransactionStarter();
}
/**
 * 
 * Interface used by SiteContext to provide a way to use a database.
 */
interface DBConnector {
	/**
	 * Set initial resource.
	 * @param resource $resource
	 */
	static function setInitialResource(resource $resource);
	
	/**
	 * Get initial resource.
	 * @return resource
	 */
	static function getInitialResource();
	
	/**
	 * General queries to the database.
	 *
	 * @param string $query
	 * @return resource
	 */
	function query(string $query);
	
	/**
	 * Begin a transaction 
	 * @return DBLocker
	 */
	public static function transactionBegin();
	
	/**
	 * Roll back a transaction started with self::transactionBegin
	 */
	public static function transactionRollback();
	
	/**
	 * Commit a transaction started with DBConnector::transactionBegin
	 * @param DBLocker $tLocker
	 */
	public static function transactionCommit(DBLocker $tLocker);
}

/**
 * Database connector to mysql db.
 * The desing is to have only one connector.
 */
class DBConnectorMySQL implements DBConnector {
	/** DB username */
	private static $username = Constants::DB_USERNAME;
	/** DB password */
	private static $password = Constants::DB_PASSWORD;
	/** DB database */
	private static $database = Constants::DB_DATABASE;
	/** DB host */
	private static $host = Constants::DB_HOST;
	
	/** Initialization queries */
	protected static $INIT_QUERIES = array ("SET NAMES 'utf8' COLLATE 'utf8_czech_ci'", 
		"SET CHARACTER SET utf8",
		"SET COLLATION_CONNECTION='utf8_czech_ci'"
	);
	
	/**
	 * @var resource - resource used across whole application execution.
	 */
	private static $initialResource;
	
	/**
	 * @see DBConnector::setInitialResource
	 */
	static function setInitialResource(resource $resource) {
		DBConnectorMySQL::$initialResource = $resource;
	}
	
	/**
	 * @see DBConnector::getInitialResource
	 */
	static function getInitialResource() {
		if (!is_resource(self::$initialResource)) {
			self::setInitialResource(mysql_connect(self::$host, self::$username, self::$password));
			foreach(self::$INIT_QUERIES as $initQuery) {
				Logger::getLogger()->debug("Init query: " . mysql_query($initQuery));
			}
		}
		return self::$initialResource;
	}
	
	/**
	 * Execute $query in the database.
	 *
	 * @throws ExceptionQueryFailed if failed.
	 * @param string $query - query to execute.
	 * @return resource - mysql resource
	 */
	function query(string $query) {
		mysql_select_db(self::$database, self::getInitialResource());
		$res = mysql_query($query);
		if (!$res) {
			throw new ExceptionQueryFailed("Query `$query` failed.");
		} else { 
			return $res;
		}
	}
	
	/**
	 * Global indicator of started/nonstarted transaction.
	 */
	private static $isInTransaction = false;
	
	/**
	 * Recursively start a database transaction.
	 * In case that transaction is already on, nothing happens.
	 * 
	 * @return TransactionLocker - which is designed to use in DBConnectorMySQL::transactionCommit.
	 * @throws Exception - in case that database returns failure.
	 */
	public static function transactionBegin() {
		if (!self::$isInTransaction) {
			$connector = SiteContext::getSiteContext()->getDBConnector();
			$res = $connector->query("BEGIN;");
			self::$isInTransaction = true;
			return new TransactionLocker(true);
		} else {
			return new TransactionLocker(false);
		}
	}
	
	/**
	 * Roll back transaction in case that transaction was turned on by call 
	 * DBConnectorMySQL::transactionBegin.
	 * 
	 * @throws Exception - in case that database returns failure.
	 */
	public static function transactionRollback() {
		if (self::$isInTransaction) {
			$connector = SiteContext::getSiteContext()->getDBConnector();
			$res = $connector->query("ROLLBACK;");
			self::$isInTransaction = false;
		}
	}
	
	/**
	 * Commit a transaction in case that $tLocker was returned by a call which really started
	 * a transaction.
	 * 
	 * @param TransactionLocker $tLocker
	 */
	public static function transactionCommit(DBLocker $tLocker) {
		if ($tLocker->getIsTransactionStarter() && self::$isInTransaction) {
			$connector = SiteContext::getSiteContext()->getDBConnector();
			$res = $connector->query("COMMIT;");
			self::$isInTransaction = false;
		}
	}
}

/**
 * Class designed to be used with transaction operations
 * implemented in class DBConnectorMySQL.
 *
 */
class TransactionLocker implements DBLocker {
	/** if is stransaction starter */
	private $isTransactionStarter;
	
	/** creates locker */
	public function __construct(bool $isStarter) {
		$this->isTransactionStarter = $isStarter;
	}
	
	/**
	 * Getter for transaction starter indication
	 */
	public function getIsTransactionStarter() {
		return $this->isTransactionStarter;
	}
}
?>
